#include "config.h"
#include <string.h>

#include "aux_types.h"
#include "aux_errno.h"
#include "aux_message.h"
#include "aux_thread.h"
#include "aux_buffer.h"

EErrNo aux_buf_init_buffer_with_lock(TxIPCBufferWithLock *ptBuf)
{
    if (!ptBuf)
        return -ERRNO_EPARAMETER_INVALID;

    memset(ptBuf, 0, sizeof(TxIPCBufferWithLock));
    if (pthread_mutex_init(&(ptBuf->tMutex), NULL))
        return ERRNO_FAIL;

    ptBuf->eLockState = BUFFER_LOCK_UNLOCKED;
    return ERRNO_SUCCESS;
}

EErrNo aux_buf_destroy_buffer_with_lock(TxIPCBufferWithLock *ptBuf)
{
    if (!ptBuf)
        return -ERRNO_EPARAMETER_INVALID;

    pthread_mutex_destroy(&(ptBuf->tMutex));
    memset(ptBuf, 0, sizeof(TxIPCBufferWithLock));

    return ERRNO_SUCCESS;
}

EErrNo aux_buf_lock(TxIPCBufferWithLock *ptBuf)
{
    if (!ptBuf)
        return -ERRNO_EPARAMETER_INVALID;

    if (pthread_mutex_lock(&(ptBuf->tMutex)))
        return -ERRNO_ELOCK_FAIL;

    ptBuf->eLockState = BUFFER_LOCK_LOCKED;
    return ERRNO_SUCCESS;
}

EErrNo aux_buf_unlock(TxIPCBufferWithLock *ptBuf)
{
    if (!ptBuf)
        return -ERRNO_EPARAMETER_INVALID;

    if (pthread_mutex_unlock(&(ptBuf->tMutex)))
        return -ERRNO_EUNLOCK_FAIL;

    ptBuf->eLockState = BUFFER_LOCK_UNLOCKED;
    return ERRNO_SUCCESS;
}

EErrNo aux_buf_clr_locked_buffer(TxIPCBufferWithLock *ptBuf)
{
    if (!ptBuf)
        return -ERRNO_EPARAMETER_INVALID;

    if (ptBuf->eLockState != BUFFER_LOCK_LOCKED)
        return -ERRNO_ENOTLOCKED;

    memset(&(ptBuf->abyBuffer), 0, sizeof(ptBuf->abyBuffer));
    ptBuf->nLength = 0;
    ptBuf->nStart = 0;

    return ERRNO_SUCCESS;
}

EErrNo aux_buf_append_locked_buffer(TxIPCBufferWithLock *ptBuf,
                                                   u8 *pabyData,
                                                   u32 nLen)
{
    if (!ptBuf || !pabyData)
        return -ERRNO_EPARAMETER_INVALID;

    if (0 == nLen)
        return ERRNO_SUCCESS;

    if (ptBuf->eLockState != BUFFER_LOCK_LOCKED)
        return -ERRNO_ENOTLOCKED;

    if (ptBuf->nLength + nLen > sizeof(ptBuf->abyBuffer))
        return -ERRNO_EMEMORY_NOT_SUFFICIENT;

    if (ptBuf->nStart + ptBuf->nLength + nLen <=
                                            sizeof(ptBuf->abyBuffer))
    {
        memcpy(&((ptBuf->abyBuffer)[ptBuf->nStart + ptBuf->nLength]),
               pabyData, nLen);
    }
    else
    {
        u32 nTailLeftCnt = sizeof(ptBuf->abyBuffer) - (ptBuf->nStart + ptBuf->nLength);
        u32 nTailStartIndex = ptBuf->nStart + ptBuf->nLength;

        memcpy(&((ptBuf->abyBuffer)[nTailStartIndex]), pabyData, nTailLeftCnt);
        memcpy(&((ptBuf->abyBuffer)[0]), &(pabyData[nTailLeftCnt]),
               nLen - nTailLeftCnt);
    }

    ptBuf->nLength += nLen;

    return ERRNO_SUCCESS;
}

EErrNo aux_buf_read_locked_buffer(TxIPCBufferWithLock *ptBuf,
                                                   u8  *pabyData,
                                                   u32 *pnLen)
{
    u32 nActualReadCnt = 0;

    if (!pnLen)
        return -ERRNO_EPARAMETER_INVALID;

    if (!ptBuf || !pabyData)
    {
        *pnLen = 0;
        return -ERRNO_EPARAMETER_INVALID;
    }

    if (0 == *pnLen)
        return ERRNO_SUCCESS;

    if (ptBuf->eLockState != BUFFER_LOCK_LOCKED)
    {
        *pnLen = 0;
        return -ERRNO_ENOTLOCKED;
    }

    if (ptBuf->nLength < *pnLen)
        nActualReadCnt = ptBuf->nLength;

    if (0 == nActualReadCnt)
    {
        *pnLen = 0;
        return ERRNO_SUCCESS;
    }

    if (ptBuf->nStart + nActualReadCnt <= sizeof(ptBuf->abyBuffer))
    {
        memcpy(pabyData,
               &((ptBuf->abyBuffer)[ptBuf->nStart]),
               nActualReadCnt);
        ptBuf->nStart += nActualReadCnt;
    }
    else
    {
        u32 nTailLeftCnt = sizeof(ptBuf->abyBuffer) - ptBuf->nStart;

        memcpy(pabyData, &((ptBuf->abyBuffer)[ptBuf->nStart]), nTailLeftCnt);
        memcpy(&(pabyData[nTailLeftCnt]), &((ptBuf->abyBuffer)[0]),
               nActualReadCnt - nTailLeftCnt);

        ptBuf->nStart = nActualReadCnt - nTailLeftCnt;
    }

    ptBuf->nLength -= nActualReadCnt;
    *pnLen = nActualReadCnt;
    return ERRNO_SUCCESS;
}

EErrNo aux_buf_lock_read(TxIPCBufferWithLock *ptBuf,
                                         u8  *pabyData,
                                         u32 *pnLen)
{
    EErrNo eRet;

    if (ERRNO_SUCCESS == aux_buf_lock(ptBuf))
    {
        eRet = aux_buf_read_locked_buffer(ptBuf, pabyData, pnLen);
        aux_buf_unlock(ptBuf);
        return eRet;
    }

    return ERRNO_FAIL;
}

EErrNo aux_buf_lock_append(TxIPCBufferWithLock *ptBuf,
                                           u8  *pabyData,
                                           u32  nLen)
{
    EErrNo eRet;

    if (ERRNO_SUCCESS == aux_buf_lock(ptBuf))
    {
        eRet = aux_buf_append_locked_buffer(ptBuf, pabyData, nLen);
        aux_buf_unlock(ptBuf);
        return eRet;
    }

    return ERRNO_FAIL;
}

EErrNo aux_buf_lock_get_size(TxIPCBufferWithLock *ptBuf,
                                            u32 *pnLen)
{
    if (ERRNO_SUCCESS == aux_buf_lock(ptBuf))
    {
        *pnLen = ptBuf->nLength;
        aux_buf_unlock(ptBuf);
        return ERRNO_SUCCESS;
    }

    return ERRNO_FAIL;
}


